/*
Redis客户端封装
提供Redis连接管理、操作封装和链路追踪功能
主要功能：
1. Redis连接池管理和初始化
2. 基础CRUD操作封装
3. 哈希表操作封装
4. OpenTelemetry链路追踪集成
*/
package rediscli

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/astaxie/beego"
	"github.com/go-redis/redis/v8"
	"gitlab.vrviu.com/inviu/backend-go-public/gopublic/otelwrap"
	"golang.org/x/net/context"
)

var (
	// _client Redis通用客户端实例
	_client redis.UniversalClient
	// _OnceInitClient 确保Redis客户端只初始化一次
	_OnceInitClient sync.Once
)

// TestBeego 测试Beego配置读取功能
// 用于调试配置文件读取是否正常
func TestBeego() {
	fmt.Printf("%s", beego.AppConfig.String("redis::hosts"))
	fmt.Printf("%s", beego.AppConfig.DefaultString("redis::username", ""))
	fmt.Printf("%s", beego.AppConfig.DefaultString("redis::password", ""))
}

// initClient 初始化Redis客户端
// 从Beego配置文件读取Redis连接参数，支持集群和哨兵模式
func initClient() {
	_client = redis.NewUniversalClient(&redis.UniversalOptions{
		Addrs:            strings.Split(beego.AppConfig.String("redis::hosts"), ","),                                    // Redis服务器地址列表，支持多个地址
		Username:         beego.AppConfig.DefaultString("redis::username", ""),                                          // Redis用户名（Redis 6.0+支持）
		Password:         beego.AppConfig.DefaultString("redis::password", ""),                                          // Redis密码
		ReadTimeout:      time.Duration(beego.AppConfig.DefaultInt("redis::read_timeout", 300)) * time.Millisecond,      // 读取超时时间
		WriteTimeout:     time.Duration(beego.AppConfig.DefaultInt("redis::write_timeout", 300)) * time.Millisecond,     // 写入超时时间
		PoolSize:         beego.AppConfig.DefaultInt("redis::conn_pool_capacity", 0),                                    // 连接池大小，0表示使用默认值
		IdleTimeout:      time.Duration(beego.AppConfig.DefaultInt("redis::conn_pool_idle_timeout", 240)) * time.Second, // 连接空闲超时时间
		MasterName:       beego.AppConfig.DefaultString("redis::mastername", ""),                                        // 哨兵模式下的主服务器名称
		SentinelPassword: beego.AppConfig.DefaultString("redis::sentinel_password", ""),                                 // 哨兵密码
	})

	// 如果启用了OpenTelemetry链路追踪，添加追踪钩子
	if beego.AppConfig.DefaultBool("redis::enable_otel", false) {
		_client.AddHook(NewTracingHook())
	}
	// rdb.HMSet()
}

// GetRedisCli 获取Redis客户端实例
// 支持传入自定义客户端，否则返回全局单例客户端
// 参数:
//
//	clients: 可选的自定义Redis客户端
//
// 返回: redis.UniversalClient - Redis通用客户端
func GetRedisCli(clients ...redis.UniversalClient) redis.UniversalClient {
	// 如果传入了自定义客户端且不为空，直接返回
	if len(clients) != 0 && clients[0] != nil {
		return clients[0]
	}

	// 否则返回全局单例客户端
	if _client == nil {
		_OnceInitClient.Do(func() {
			initClient()
		})
	}
	return _client
}

// WriteDataToRedis 写入数据到Redis
// 封装了Redis的SET操作
// 参数:
//
//	ctx: 上下文
//	key: Redis键
//	value: 要存储的值
//	clients: 可选的自定义Redis客户端
//
// 返回: error - 操作错误信息
func WriteDataToRedis(ctx context.Context, key string, value interface{}, clients ...redis.UniversalClient) error {
	_, err := GetRedisCli(clients...).Set(ctx, key, value, 0).Result()
	return err
}

// RedisGetStringWithCtx 从Redis获取字符串值（带上下文）
// 参数:
//
//	ctx: 上下文
//	key: Redis键
//	clients: 可选的自定义Redis客户端
//
// 返回: (string, error) - 字符串值和错误信息
func RedisGetStringWithCtx(ctx context.Context, key string, clients ...redis.UniversalClient) (string, error) {
	get := GetRedisCli(clients...).Get(ctx, key)
	return get.Result()
}

// RedisGetWithCtx 从Redis获取值并扫描到指定对象（带上下文）
// 参数:
//
//	ctx: 上下文
//	key: Redis键
//	value: 目标对象指针
//	clients: 可选的自定义Redis客户端
//
// 返回: error - 操作错误信息
func RedisGetWithCtx(ctx context.Context, key string, value interface{}, clients ...redis.UniversalClient) error {
	get := GetRedisCli(clients...).Get(ctx, key)
	return get.Scan(value)
}

// RedisGetIntWithCtx 从Redis获取整数值（带上下文）
// 参数:
//
//	ctx: 上下文
//	key: Redis键
//	clients: 可选的自定义Redis客户端
//
// 返回: (int, error) - 整数值和错误信息
func RedisGetIntWithCtx(ctx context.Context, key string, clients ...redis.UniversalClient) (int, error) {
	get := GetRedisCli(clients...).Get(ctx, key)
	return get.Int()
}

// RedisHGetString 获取哈希表指定字段的字符串值
// 参数:
//
//	key: 哈希表键名
//	field: 字段名
//	clients: 可选的自定义Redis客户端
//
// 返回: (string, error) - 字符串值和错误信息
func RedisHGetString(key, field string, clients ...redis.UniversalClient) (string, error) {
	return RedisHGetStringWithCtx(otelwrap.Skip(), key, field, clients...)
}

// RedisHGetStringWithCtx 获取哈希表指定字段的字符串值（带上下文）
// 参数:
//
//	ctx: 上下文
//	key: 哈希表键名
//	field: 字段名
//	clients: 可选的自定义Redis客户端
//
// 返回: (string, error) - 字符串值和错误信息
func RedisHGetStringWithCtx(ctx context.Context, key, field string, clients ...redis.UniversalClient) (string, error) {
	hget := GetRedisCli(clients...).HGet(ctx, key, field)
	return hget.Result()
}

// RedisHGetInt 获取哈希表指定字段的整数值
// 参数:
//
//	key: 哈希表键名
//	field: 字段名
//	clients: 可选的自定义Redis客户端
//
// 返回: (int, error) - 整数值和错误信息
func RedisHGetInt(key, field string, clients ...redis.UniversalClient) (int, error) {
	return RedisHGetIntWithCtx(otelwrap.Skip(), key, field, clients...)
}

// RedisHGetIntWithCtx 获取哈希表指定字段的整数值（带上下文）
// 参数:
//
//	ctx: 上下文
//	key: 哈希表键名
//	field: 字段名
//	clients: 可选的自定义Redis客户端
//
// 返回: (int, error) - 整数值和错误信息
func RedisHGetIntWithCtx(ctx context.Context, key, field string, clients ...redis.UniversalClient) (int, error) {
	hget := GetRedisCli(clients...).HGet(ctx, key, field)
	return hget.Int()
}

// RedisHMGet 批量获取哈希表多个字段的值
// 参数:
//
//	key: 哈希表键名
//	field: 字段名列表
//	clients: 可选的自定义Redis客户端
//
// 返回: ([]interface{}, error) - 值列表（类型为string或nil）和错误信息
func RedisHMGet(key string, field []string, clients ...redis.UniversalClient) ([]interface{}, error) {
	return RedisHMGetWithCtx(otelwrap.Skip(), key, field, clients...)
}

// RedisHMGetWithCtx 批量获取哈希表多个字段的值（带上下文）
// 参数:
//
//	ctx: 上下文
//	key: 哈希表键名
//	field: 字段名列表
//	clients: 可选的自定义Redis客户端
//
// 返回: ([]interface{}, error) - 值列表（类型为string或nil）和错误信息
func RedisHMGetWithCtx(ctx context.Context, key string, field []string, clients ...redis.UniversalClient) ([]interface{}, error) {
	return GetRedisCli(clients...).HMGet(ctx, key, field...).Result()
}
